# -*- coding: UTF-8 -*-
'''
@Project ：automation_-interface_testing
@File    ：yaml_config.py
@IDE     ：PyCharm
@Author  ：思亦忆
@Date    ：2025/9/5 星期五 21:58:53
@summary:配置文件管理类 - 支持YAML和JSON格式
'''

import os
import re
import yaml
import json
from pathlib import Path
from typing import Any, Dict, Optional, List, Union
from copy import deepcopy
import threading
from src.common.logs import get_logger


class ConfigManager:
    """
    配置文件管理类 - 支持YAML和JSON格式
    封装配置文件的读取、写入和修改操作
    支持嵌套键访问、环境变量替换、多环境配置等功能
    """

    def __init__(self, config_path: Optional[str] = None,
                 environment: Optional[str] = None,
                 enable_env_substitution: bool = True,
                 config_type: str = 'auto') -> None:
        """
        初始化配置管理类

        Args:
            config_path: 配置文件路径，如果为None则使用默认路径
            environment: 环境名称（如'test', 'dev'），用于自动选择配置文件
            enable_env_substitution: 是否启用环境变量替换，默认True
            config_type: 配置文件类型，'auto'自动检测，'yaml'或'json'

        Raises:
            FileNotFoundError: 配置文件不存在时抛出
            ValueError: 配置文件格式不支持时抛出
        """
        self.logger = get_logger(module_name='config_manager')
        self.enable_env_substitution = enable_env_substitution
        self.config_type = config_type
        self._lock = threading.RLock()

        if config_path is None:
            current_file = Path(__file__)
            project_root = current_file.parent.parent.parent

            if environment:
                # 尝试查找环境相关的配置文件
                possible_files = [
                    project_root / 'config' / f'config_{environment}.yaml',
                    project_root / 'config' / f'config_{environment}.yml',
                    project_root / 'config' / f'config_{environment}.json',
                    project_root / 'config' / f'api_config.json'
                ]

                for file_path in possible_files:
                    if file_path.exists():
                        config_path = file_path
                        self.logger.info(f"找到环境配置文件: {config_path}")
                        break
                else:
                    self.logger.warning(f"未找到环境配置文件，使用默认配置")
                    config_path = project_root / 'config' / 'config.yaml'
            else:
                # 默认使用API配置
                config_path = project_root / 'config' / 'api_config.json'
        else:
            config_path = Path(config_path)

        self.path = config_path
        self.data: Dict[str, Any] = {}
        self.original_data: Dict[str, Any] = {}

        # 验证配置文件是否存在
        if not self.path.exists():
            raise FileNotFoundError(f"配置文件不存在: {self.path}")

        # 确定配置文件类型
        if config_type == 'auto':
            self._detect_config_type()
        else:
            self.config_type = config_type.lower()

        # 验证文件扩展名
        valid_extensions = ['.yaml', '.yml', '.json']
        if self.path.suffix.lower() not in valid_extensions:
            raise ValueError(f"文件格式不支持: {self.path.suffix}")

        # 加载配置文件
        self._load_config()

    def _detect_config_type(self) -> None:
        """自动检测配置文件类型"""
        extension = self.path.suffix.lower()
        if extension in ['.yaml', '.yml']:
            self.config_type = 'yaml'
        elif extension == '.json':
            self.config_type = 'json'
        else:
            # 通过文件内容检测
            try:
                with open(self.path, 'r', encoding='utf-8') as f:
                    content = f.read().strip()
                    if content.startswith('{') and content.endswith('}'):
                        self.config_type = 'json'
                    else:
                        self.config_type = 'yaml'
            except:
                self.config_type = 'yaml'  # 默认使用YAML

        self.logger.info(f"检测到配置文件类型: {self.config_type}")

    def _load_config(self) -> None:
        """加载并解析配置文件"""
        try:
            with open(self.path, 'r', encoding='utf-8') as cfg:
                file_data = cfg.read()

            if self.enable_env_substitution:
                file_data = self._substitute_env_vars(file_data)

            if self.config_type == 'json':
                self.data = json.loads(file_data) or {}
            else:
                self.data = yaml.safe_load(file_data) or {}

            self.original_data = deepcopy(self.data)
            self.logger.info(f"成功加载{self.config_type.upper()}配置文件: {self.path}")

        except (yaml.YAMLError, json.JSONDecodeError) as e:
            error_msg = f"配置文件格式错误: {e}"
            self.logger.error(error_msg)
            raise ValueError(error_msg)
        except Exception as e:
            error_msg = f"加载配置文件失败: {e}"
            self.logger.error(error_msg)
            raise Exception(error_msg)

    def _substitute_env_vars(self, content: str) -> str:
        """替换配置文件中的环境变量"""
        def replace_env_var(match):
            var_expr = match.group(1)
            if ':-' in var_expr:
                var_name, default_value = var_expr.split(':-', 1)
                return os.getenv(var_name.strip(), default_value.strip())
            else:
                return os.getenv(var_expr.strip(), match.group(0))

        pattern = r'\$\{([^}]+)\}'
        return re.sub(pattern, replace_env_var, content)

    # 🔧 新增：专门用于API配置的方法
    def get_api_config(self, api_name: str = None) -> Dict[str, Any]:
        """
        获取API配置信息

        Args:
            api_name: API名称，如'login_api'，为None时返回所有API配置

        Returns:
            Dict: API配置信息
        """
        if api_name:
            return self.get_nested_value(f'api_config.{api_name}', {})
        else:
            return self.get_value('api_config', {})

    def get_login_api_config(self) -> Dict[str, Any]:
        """获取登录API配置"""
        return self.get_api_config('login_api')

    def get_test_accounts(self, account_type: str = 'valid_user') -> Dict[str, Any]:
        """
        获取测试账号配置

        Args:
            account_type: 账号类型，如'valid_user', 'invalid_user', 'admin_user'

        Returns:
            Dict: 账号配置信息
        """
        return self.get_nested_value(f'api_config.login_api.test_accounts.{account_type}', {})

    def get_environment_config(self, env: str = 'test') -> Dict[str, Any]:
        """
        获取环境配置

        Args:
            env: 环境名称，如'test', 'production'

        Returns:
            Dict: 环境配置信息
        """
        return self.get_nested_value(f'environment.{env}', {})

    def get_test_cases(self, case_type: str = 'login_cases') -> List[Dict[str, Any]]:
        """
        获取测试用例配置

        Args:
            case_type: 用例类型，如'login_cases'

        Returns:
            List: 测试用例列表
        """
        return self.get_nested_value(f'test_data.{case_type}', [])

    def get_base_url(self, environment: str = 'test') -> str:
        """获取基础URL"""
        env_config = self.get_environment_config(environment)
        return env_config.get('base_url', '')

    def get_request_headers(self, api_name: str = 'login_api') -> Dict[str, Any]:
        """获取请求头配置"""
        api_config = self.get_api_config(api_name)
        return api_config.get('headers', {})

    def get_request_body_template(self, api_name: str = 'login_api') -> Dict[str, Any]:
        """获取请求体模板"""
        api_config = self.get_api_config(api_name)
        return api_config.get('request_body', {})

    # 保留原有的通用方法
    def get_all(self) -> Dict[str, Any]:
        """获取所有配置数据"""
        return self.data

    def get_value(self, section: str, key: str, default: Any = None) -> Any:
        """获取指定section下的key对应的值"""
        with self._lock:
            try:
                return self.data[section][key]
            except KeyError:
                if default is not None:
                    return default
                raise KeyError(f"配置项不存在: section='{section}', key='{key}'")

    def get_nested_value(self, key_path: str, default: Any = None) -> Any:
        """获取嵌套配置值，支持点号分隔的路径"""
        with self._lock:
            keys = key_path.split('.')
            value = self.data

            try:
                for key in keys:
                    if isinstance(value, dict):
                        value = value[key]
                    else:
                        if default is not None:
                            return default
                        raise KeyError(f"配置路径不存在: {key_path}")
                return value
            except (KeyError, TypeError):
                if default is not None:
                    return default
                raise KeyError(f"配置路径不存在: {key_path}")

    def save(self, filepath: Optional[str] = None, backup: bool = True) -> bool:
        """保存配置到文件"""
        with self._lock:
            save_path = Path(filepath) if filepath else self.path

            try:
                if backup and save_path.exists() and save_path == self.path:
                    backup_path = save_path.with_suffix(f'.{save_path.suffix}.bak')
                    import shutil
                    shutil.copy2(save_path, backup_path)
                    self.logger.debug(f"已备份原文件到: {backup_path}")

                save_path.parent.mkdir(parents=True, exist_ok=True)

                with open(save_path, 'w', encoding='utf-8') as f:
                    if self.config_type == 'json':
                        json.dump(self.data, f, indent=2, ensure_ascii=False)
                    else:
                        yaml.dump(
                            self.data,
                            f,
                            default_flow_style=False,
                            allow_unicode=True,
                            indent=2,
                            sort_keys=False
                        )

                self.original_data = deepcopy(self.data)
                self.logger.info(f"配置已保存到: {save_path}")
                return True

            except Exception as e:
                error_msg = f"保存配置文件失败: {e}"
                self.logger.error(error_msg)
                raise IOError(error_msg)

    # 其他原有方法保持不变...
    def set_value(self, section: str, key: str, value: Any) -> bool:
        """设置配置值"""
        with self._lock:
            if section not in self.data:
                self.data[section] = {}
            self.data[section][key] = value
            return True

    def validate_api_config(self) -> Dict[str, bool]:
        """验证API配置完整性"""
        required_keys = [
            'api_config.login_api.url',
            'api_config.login_api.method',
            'api_config.login_api.request_body.account',
            'api_config.login_api.request_body.password'
        ]

        return self.validate_required_keys(required_keys)

    def validate_required_keys(self, required_keys: List[str]) -> Dict[str, bool]:
        """验证必需的配置键是否存在"""
        results = {}
        for key_path in required_keys:
            try:
                self.get_nested_value(key_path)
                results[key_path] = True
            except KeyError:
                results[key_path] = False
        return results


# 全局单例字典
_config_instances: Dict[str, ConfigManager] = {}
_config_lock = threading.Lock()


def get_config_manager(config_path: Optional[str] = None,
                       environment: Optional[str] = None,
                       force_reload: bool = False) -> ConfigManager:
    """获取配置管理器实例"""
    if config_path:
        key = str(Path(config_path).absolute())
    elif environment:
        key = f"env_{environment}"
    else:
        key = "default"

    with _config_lock:
        if force_reload or key not in _config_instances:
            _config_instances[key] = ConfigManager(config_path, environment)
        return _config_instances[key]


def get_api_config(api_name: str = None, environment: str = 'test') -> Dict[str, Any]:
    """快速获取API配置"""
    config = get_config_manager(environment=environment)
    return config.get_api_config(api_name)


def get_test_account(account_type: str = 'valid_user', environment: str = 'test') -> Dict[str, Any]:
    """快速获取测试账号"""
    config = get_config_manager(environment=environment)
    return config.get_test_accounts(account_type)


# 测试代码
if __name__ == '__main__':
    # 测试API配置读取
    try:
        config = ConfigManager()
        print("=" * 60)
        print("🎯 API配置测试")
        print("=" * 60)

        # 测试登录API配置
        login_config = config.get_login_api_config()
        print(f"登录API配置: {login_config.get('name')}")
        print(f"请求URL: {login_config.get('url')}")
        print(f"请求方法: {login_config.get('method')}")

        # 测试账号信息
        valid_account = config.get_test_accounts('valid_user')
        print(f"测试账号: {valid_account.get('account')}")

        # 测试环境配置
        env_config = config.get_environment_config('test')
        print(f"测试环境URL: {env_config.get('base_url')}")

        # 验证配置完整性
        validation = config.validate_api_config()
        print("配置验证结果:")
        for key, valid in validation.items():
            status = "✅" if valid else "❌"
            print(f"  {status} {key}")

    except Exception as e:
        print(f"❌ 配置测试失败: {e}")